﻿#pragma warning disable 649

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Watermelon
{
    public class EditorSceneController : MonoBehaviour
    {
        private const string PREFS_WIDTH_KEY = "EditorCameraOverrideWidth";

#if UNITY_EDITOR
        private static EditorSceneController instance;
        public static EditorSceneController Instance { get => instance; }

        [SerializeField] GameObject container;
        [SerializeField] private GameObject backgroundContainer;
        [SerializeField] private List<GameObject> unpickableObjects;
        [SerializeField] float gizmoRadius;
        [SerializeField] float gizmoThickness;
        [SerializeField] Color gizmoColor;
        [SerializeField] GameObject screwPrefab;
        [SerializeField] List<PlankData> plankData;
        [SerializeField] Color[] layerColors;
        private SavableHole holeRef1;
        private SavableHole holeRef2;
        private bool isQuickModeEnabled;
        private int currentLayerIndex;
        private int maxLayerIndex;

        private bool stageChenged;
        private HoleData[] holeDataCached;
        private PlankLevelData[] plankDataCached;

        public GameObject Container { get => container; set => container = value; }
        public GameObject ScrewPrefab { get => screwPrefab; set => screwPrefab = value; }
        public List<PlankData> PlankData { get => plankData; set => plankData = value; }
        public bool IsQuickModeEnabled { get => isQuickModeEnabled; set => isQuickModeEnabled = value; }
        public Color[] LayerColors { get => layerColors; set => layerColors = value; }
        public int CurrentLayerIndex
        {
            get => currentLayerIndex; set
            {
                currentLayerIndex = value;
                UpdatePlanks();
            }
        }
        public int MaxLayerIndex { get => maxLayerIndex; set => maxLayerIndex = value; }

        public EditorSceneController()
        {
            instance = this;
            SceneView.duringSceneGui += DuringSceneGui;
            Selection.selectionChanged += SelectionChanged;
            EditorApplication.hierarchyChanged += UpdateHoleSelection;
        }



        public void SpawnHole(GameObject prefab, Vector3 position, bool hasScrew = false)
        {
            GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
            gameObject.hideFlags = HideFlags.DontSave;
            gameObject.transform.SetParent(container.transform);
            gameObject.transform.position = position;
            gameObject.transform.rotation = Quaternion.identity;
            gameObject.name = prefab.name + " ( Child # " + container.transform.childCount + ")";

            SpriteRenderer spriteRenderer = gameObject.GetComponentInChildren<SpriteRenderer>();

            if (spriteRenderer != null)
            {
                spriteRenderer.sortingOrder = 30000;
            }

            SavableHole savableItem = gameObject.AddComponent<SavableHole>();
            savableItem.hideFlags = HideFlags.DontSave;
            savableItem.HasScrew = hasScrew;

            SelectGameObject(gameObject);
        }

        //used when level loads in level editor
        public void SpawnPlank(PlankLevelData data, GameObject prefab)
        {
            GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
            gameObject.hideFlags = HideFlags.DontSave;
            gameObject.transform.SetParent(container.transform);
            gameObject.transform.position = data.Position;
            gameObject.transform.rotation = Quaternion.Euler(data.Rotation);
            gameObject.name = prefab.name + "(el # " + container.transform.childCount + ")";
            gameObject.transform.localScale = data.Scale;

            SavablePlank savableItem = gameObject.AddComponent<SavablePlank>();
            savableItem.PlankType = data.PlankType;
            savableItem.PlankLayer = data.PlankLayer;
            savableItem.ScrewsPositions = data.ScrewsPositions;
            savableItem.hideFlags = HideFlags.DontSave;

            SelectGameObject(gameObject);

            if (data.PlankLayer > maxLayerIndex)
            {
                maxLayerIndex = data.PlankLayer;
            }
        }

        public void SpawnPlankWithUndo(PlankLevelData data, GameObject prefab)
        {
            Undo.IncrementCurrentGroup();

            GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject;

            Undo.RegisterCreatedObjectUndo(gameObject, "SpawnPlankWithUndo");
            Undo.SetTransformParent(gameObject.transform, container.transform, "SpawnPlankWithUndo");
            Undo.RegisterFullObjectHierarchyUndo(gameObject.transform, "SpawnPlankWithUndo");

            gameObject.hideFlags = HideFlags.DontSave;
            gameObject.transform.position = data.Position;
            gameObject.transform.rotation = Quaternion.Euler(data.Rotation);
            gameObject.transform.localScale = data.Scale;
            gameObject.name = prefab.name + "(el # " + container.transform.childCount + ")";

            SavablePlank savableItem = gameObject.AddComponent<SavablePlank>();

            Undo.RegisterCreatedObjectUndo(savableItem, "SpawnPlankWithUndo");
            Undo.RecordObject(savableItem, "SpawnPlankWithUndo");

            savableItem.hideFlags = HideFlags.DontSave;
            savableItem.PlankType = data.PlankType;
            savableItem.PlankLayer = data.PlankLayer;
            savableItem.ScrewsPositions = data.ScrewsPositions;

            SelectGameObject(gameObject);

            Undo.SetCurrentGroupName("SpawnPlankWithUndo");

            if (data.PlankLayer > maxLayerIndex)
            {
                maxLayerIndex = data.PlankLayer;
            }
        }

        public void SpawnBackground(GameObject prefab)
        {
            for (int i = backgroundContainer.transform.childCount - 1; i >= 0; i--)
            {
                DestroyImmediate(backgroundContainer.transform.GetChild(i).gameObject);
            }

            GameObject gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
            gameObject.hideFlags = HideFlags.DontSave;
            gameObject.transform.SetParent(backgroundContainer.transform);
            gameObject.transform.position = Vector3.forward;
            gameObject.transform.rotation = Quaternion.identity;
            gameObject.transform.localScale = Vector3.one;

            SceneVisibilityManager.instance.DisablePicking(gameObject, true);
        }

        public void SelectGameObject(GameObject selectedGameObject)
        {
            Selection.activeGameObject = selectedGameObject;
        }

        public void Clear()
        {
            if(container == null)
            {
                return;
            }

            for (int i = container.transform.childCount - 1; i >= 0; i--)
            {
                DestroyImmediate(container.transform.GetChild(i).gameObject);
            }

            for (int i = 0; i < unpickableObjects.Count; i++)
            {
                SceneVisibilityManager.instance.DisablePicking(unpickableObjects[i], true);
            }

            currentLayerIndex = 0;
            maxLayerIndex = 0;
        }

        public void UpdateHoleSelection()
        {
            try
            {
                SavableHole[] savableItems = container.GetComponentsInChildren<SavableHole>();

                for (int i = 0; i < savableItems.Length; i++)
                {
                    SceneVisibilityManager.instance.DisablePicking(savableItems[i].gameObject, true);
                    SceneVisibilityManager.instance.EnablePicking(savableItems[i].gameObject, false);
                    SceneVisibilityManager.instance.EnablePicking(savableItems[i].transform.GetChild(0).gameObject, false);
                }
            }
            catch
            {

            }
        }

        public HoleData[] GetHoleData()
        {
            SavableHole[] savableItems = container.GetComponentsInChildren<SavableHole>();
            HoleData[] result = new HoleData[savableItems.Length];

            for (int i = 0; i < result.Length; i++)
            {
                result[i] = new HoleData(savableItems[i].transform.position, savableItems[i].HasScrew);
            }

            return result;
        }

        public void UpdatePlanks()
        {
            SavablePlank[] savableItems = container.GetComponentsInChildren<SavablePlank>();

            for (int i = 0; i < savableItems.Length; i++)
            {
                savableItems[i].LayerUpdate();
            }
        }

        public PlankLevelData[] GetPlankData()
        {
            SavablePlank[] savableItems = container.GetComponentsInChildren<SavablePlank>();
            UpdateScrewPositions(savableItems);
            PlankLevelData[] result = new PlankLevelData[savableItems.Length];

            for (int i = 0; i < result.Length; i++)
            {
                result[i] = new PlankLevelData(savableItems[i].PlankType, savableItems[i].PlankLayer, savableItems[i].transform.position, savableItems[i].transform.rotation.eulerAngles, savableItems[i].transform.localScale, savableItems[i].ScrewsPositions);
            }

            return result;
        }

        public void UpdateScrewPositions(SavablePlank[] planks)
        {
            SavableHole[] holes = container.GetComponentsInChildren<SavableHole>();
            PlankBehavior plankBehavior;

            for (int i = 0; i < planks.Length; i++)
            {
                planks[i].ScrewsPositions.Clear();
                plankBehavior = planks[i].GetComponent<PlankBehavior>();

                for (int j = 0; j < holes.Length; j++)
                {
                    if (holes[j].HasScrew && (plankBehavior.DoesPointOverlapsCollider(holes[j].transform.position)))
                    {
                        Vector3 screwPosition = holes[j].transform.position - planks[i].transform.position;

                        planks[i].ScrewsPositions.Add(RotateVectorAroundPivot(screwPosition, planks[i].transform.position, -Vector3.forward * planks[i].transform.eulerAngles.z));
                    }
                }
            }
        }

        private Vector3 RotateVectorAroundPivot(Vector3 vector, Vector3 pivot, Vector3 angles)
        {
            return Quaternion.Euler(angles) * vector; // rotate it
        }



        public void OnDrawGizmos()
        {
            if (holeRef1 != null)
            {
                Color backupColor = Handles.color;
                Handles.color = gizmoColor;
                Handles.DrawWireDisc(holeRef1.transform.position, Vector3.forward, gizmoRadius, gizmoThickness);

                SavableHole hole = Selection.activeGameObject.GetComponent<SavableHole>();

                if ((hole != null) && (hole != holeRef1))
                {
                    Handles.DrawLine(holeRef1.transform.position, hole.transform.position, gizmoThickness);
                }

                Handles.color = backupColor;

            }
        }

        [Button]
        public void SaveCameraPosition()
        {
            PlayerPrefs.SetFloat(PREFS_WIDTH_KEY, SceneView.lastActiveSceneView.size);
        }

        public void SetUpCamera()
        {
            SceneView sceneView = SceneView.lastActiveSceneView;
            sceneView.AlignViewToObject(transform);

            if (PlayerPrefs.HasKey(PREFS_WIDTH_KEY))
            {
                sceneView.size = PlayerPrefs.GetFloat(PREFS_WIDTH_KEY, 6f);
            }
            else
            {
                sceneView.size = PlayerPrefs.GetFloat("CameraScalerWidth", 6f);
            }

            
            SceneView.RepaintAll();
        }

        public void ShowEditorOverlay()
        {
            SceneView.lastActiveSceneView.TryGetOverlay("Level editor overlay", out var overlay);

            if (overlay != null)
            {
                overlay.displayed = true;
                overlay.collapsed = false;
            }
        }

        public void HideEditorOverlay()
        {
            SceneView.lastActiveSceneView.TryGetOverlay("Level editor overlay", out var overlay);

            if (overlay != null)
            {
                overlay.displayed = false;
            }
        }

        public void SetHoleRef(SavableHole holeRef)
        {
            if (holeRef1 == null)
            {
                holeRef1 = holeRef;
                SceneView.RepaintAll();// updating scene to get gizmo
            }
            else
            {
                if (holeRef == holeRef1)
                {
                    return;
                }

                holeRef2 = holeRef;
                CreatePlank();
            }
        }

        // quick mode
        private void CreatePlank()
        {
            SceneView.RepaintAll();// getting rid of the gizmos
            Debug.Log($"Create plank called from hole1: {holeRef1.transform.position} to hole2: {holeRef2.transform.position}");

            float holesDifference = (holeRef1.transform.position - holeRef2.transform.position).magnitude;

            bool suitablePlankFound = false;
            PlankType plankToSpawn = PlankType.Normal_size_1;
            GameObject prefab;

            for (int i = 0; i < plankData.Count; i++)
            {
                if (plankData[i].UseInQuickMode && plankData[i].QuickModeSize > 0)
                {
                    if (plankData[i].QuickModeSize > holesDifference)
                    {
                        suitablePlankFound = true;
                        plankToSpawn = plankData[i].Type;
                        prefab = plankData[i].Prefab;
                        Vector3 spawnPosition = holeRef1.transform.position + (holeRef2.transform.position - holeRef1.transform.position) * 0.5f;

                        Quaternion rightRotation = Quaternion.LookRotation(Vector3.right);
                        Quaternion secondHoleRotation = Quaternion.LookRotation((holeRef2.transform.position - holeRef1.transform.position).normalized);

                        float angle = Vector2.Angle(Vector3.right, holeRef2.transform.position - holeRef1.transform.position);
                        // float angle = Quaternion.Angle(rightRotation, secondHoleRotation);

                        if (holeRef1.transform.position.x < holeRef2.transform.position.x && holeRef1.transform.position.y > holeRef2.transform.position.y)
                        {
                            angle = -angle;
                        }

                        if (holeRef1.transform.position.x > holeRef2.transform.position.x && holeRef1.transform.position.y > holeRef2.transform.position.y)
                        {
                            angle = -angle;
                        }

                        if (!holeRef1.HasScrew)
                        {
                            holeRef1.HasScrew = true;
                        }

                        if (!holeRef2.HasScrew)
                        {
                            holeRef2.HasScrew = true;
                        }

                        SpawnPlankWithUndo(new PlankLevelData(plankToSpawn, currentLayerIndex, spawnPosition, Vector3.forward * angle, Vector3.one, new List<Vector3>()), prefab);

                        break;
                    }
                }
            }

            if (!suitablePlankFound)
            {
                Debug.Log("Suitable plank wasn't found");
            }

            holeRef1 = null;
            holeRef2 = null;
        }

        private static void DuringSceneGui(SceneView sceneView)
        {
            if (Event.current.keyCode == KeyCode.A)
            {
                if (Event.current.type == EventType.KeyDown)
                {
                    Instance.IsQuickModeEnabled = true;
                }
                else if (Event.current.type == EventType.KeyUp)
                {
                    Instance.IsQuickModeEnabled = false;
                    Instance.holeRef1 = null;
                    Instance.holeRef2 = null;
                }
            }

            if ((Event.current.keyCode == KeyCode.D) && (Event.current.type == EventType.KeyDown))
            {
                if (Selection.activeGameObject != null)
                {
                    SavableHole hole = Selection.activeGameObject.GetComponent<SavableHole>();

                    if (hole != null)
                    {
                        hole.HasScrew = !hole.HasScrew;
                    }
                }
            }
        }

        private void SelectionChanged()
        {
            if (!IsQuickModeEnabled)
            {
                return;
            }

            if (Selection.activeGameObject != null)
            {
                SavableHole hole = Selection.activeGameObject.GetComponent<SavableHole>();

                if (hole != null)
                {
                    SetHoleRef(hole);
                }

            }
        }

        private void OnDestroy()
        {
            Unsubscribe();
        }

        public void Unsubscribe()
        {
            HideEditorOverlay();
            SceneView.duringSceneGui -= DuringSceneGui;
            Selection.selectionChanged -= SelectionChanged;
            EditorApplication.hierarchyChanged -= UpdateHoleSelection;
        }

        public bool IsStageChanged()
        {
            if (stageChenged)
            {
                return stageChenged;
            }

            if(container == null)
            {
                return false;
            }

            if (!holeDataCached.SequenceEqual(GetHoleData()))
            {
                stageChenged = true;
                return stageChenged;
            }

            if (!plankDataCached.SequenceEqual(GetPlankData()))
            {
                stageChenged = true;
                return stageChenged;
            }

            return stageChenged;
        }

        public void RegisterStageState()
        {
            stageChenged = false;
            holeDataCached = GetHoleData();
            plankDataCached = GetPlankData();
        }




#endif
    }
}
